/*===========================================================================
  Copyright (C) 2012-2013 by the Okapi Framework contributors
-----------------------------------------------------------------------------
  This library is free software; you can redistribute it and/or modify it 
  under the terms of the GNU Lesser General Public License as published by 
  the Free Software Foundation; either version 2.1 of the License, or (at 
  your option) any later version.

  This library is distributed in the hope that it will be useful, but 
  WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
  General Public License for more details.

  You should have received a copy of the GNU Lesser General Public License 
  along with this library; if not, write to the Free Software Foundation, 
  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

  See also the full LGPL text here: http://www.gnu.org/copyleft/lesser.html
===========================================================================*/

package net.sf.okapi.applications.lynx;

import java.io.File;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import net.sf.okapi.lib.xliff2.URIParser;
import net.sf.okapi.lib.xliff2.URIPrefixes;
import net.sf.okapi.lib.xliff2.Util;
import net.sf.okapi.lib.xliff2.core.MTag;
import net.sf.okapi.lib.xliff2.core.Tag;
import net.sf.okapi.lib.xliff2.core.CTag;
import net.sf.okapi.lib.xliff2.core.ExtAttribute;
import net.sf.okapi.lib.xliff2.core.ExtElement;
import net.sf.okapi.lib.xliff2.core.Tags;
import net.sf.okapi.lib.xliff2.core.Note;
import net.sf.okapi.lib.xliff2.core.Part;
import net.sf.okapi.lib.xliff2.core.Segment;
import net.sf.okapi.lib.xliff2.core.StartFileData;
import net.sf.okapi.lib.xliff2.core.StartGroupData;
import net.sf.okapi.lib.xliff2.core.TagType;
import net.sf.okapi.lib.xliff2.core.Unit;
import net.sf.okapi.lib.xliff2.document.XLIFFDocument;
import net.sf.okapi.lib.xliff2.reader.Event;
import net.sf.okapi.lib.xliff2.reader.URIContext;
import net.sf.okapi.lib.xliff2.reader.XLIFFReader;

public class FragmentFinder {

	private File extraPrefixes;
	
	public FragmentFinder (File extraPrefixes) {
		this.extraPrefixes = extraPrefixes;
	}
	
	public void verifyReferences (List<File> list) {
		XLIFFReader reader = null;
		URIParser up = new URIParser(extraPrefixes);
		try {
			// Create the reader
			reader = new XLIFFReader(XLIFFReader.VALIDATION_MAXIMAL);
			// Process all files
			for ( File input : list ) {
				// Show the input path
				System.out.println("Input: "+input.getAbsolutePath());
				// Load the document
				XLIFFDocument doc = new XLIFFDocument();
				doc.load(input, XLIFFReader.VALIDATION_MAXIMAL);
				// Check the annotations
				Iterator<Event> iter = doc.createEventIterator();
				while ( iter.hasNext() ) {
					Event event = iter.next();
					if ( !event.isUnit()) continue;
					// Look for ref attributes in units
					Unit unit = event.getUnit();
					checkRefAttributes(doc, up, event.getURIContext(), unit.getStore().getSourceTags(), true);
					checkRefAttributes(doc, up, event.getURIContext(), unit.getStore().getTargetTags(), false);
				}
				// Open the input
//				reader.open(input.toURI());
//				while ( reader.hasNext() ) {
//					Event event = reader.next();
//					if ( !event.isUnit()) continue;
//					// Look for ref attributes in units
//					Unit unit = event.getUnit();
//					checkRefAttributes(up, event.getURIContext(), unit.getStore().getSourceMarkers());
//					checkRefAttributes(up, event.getURIContext(), unit.getStore().getTargetMarkers());
//				}
//				reader.close();
			}			
		}
		finally {
			if ( reader != null ) reader.close();
		}
	}
	
	private void checkRefAttributes (XLIFFDocument doc,
		URIParser up,
		URIContext context,
		Tags markers,
		boolean source)
	{
		for ( Tag bm : markers ) {
			if ( bm.isCode() || ( bm.getTagType() == TagType.CLOSING )) continue;
			MTag am = (MTag)bm;
			String ref = am.getRef();
			if ( ref == null ) continue;
			System.out.println(String.format("- File=%s, Unit=%s, annotation=%s (%s) ref=\"%s\":",
				context.getFileId(), context.getUnitId(), am.getId(), (source ? "source" : "target"), ref));
			try {
				// set the context and validate the syntax
				up.setURL(ref, context.getFileId(), context.getGroupId(), context.getUnitId());
				System.out.print("Valid syntax. ");
				// Try to resolve the reference
				if ( !up.isFragmentOnly() ) {
					if ( up.isXLIFF() ) System.out.println("External XLIFF reference.");
					else System.out.println("External non-XLIFF reference.");
					continue;
				}
				up.complementReference();
				Object obj = doc.fetchReference(up);
				if ( obj == null ) {
					System.out.println("Reference not found.");
				}
				else {
					System.out.println("Reference found ("+obj.getClass().getName()+" at \""+up.getURI().getFragment()+"\")");
				}
			}
			catch ( Throwable e ) {
				System.out.println("ERROR: "+e.getLocalizedMessage());
				continue;
			}
		}
	}
	
	public void listPrefixes () {
		URIPrefixes uriPrefixes = new URIPrefixes(extraPrefixes);
		Map<String, List<String>> map = uriPrefixes.get();
		System.out.println("Extensions and modules prefixes allowed in fragment identifiers:");
		for ( String key : map.keySet() ) {
			for ( String uri : map.get(key) ) {
				System.out.println("- \""+key+"\" = \""+uri+"\"");
			}
		}
	}
	
	public void findFragment (List<File> list,
		String uriFragment)
	{
		XLIFFReader reader = null;
		try {
			// Fix the parameter if needed
			if ( !uriFragment.startsWith("#") ) uriFragment = "#"+uriFragment;
			// Parse the URI fragment
			URIParser up = new URIParser(extraPrefixes);
			up.setURL(uriFragment, null, null, null);
			// Create the reader
			reader = new XLIFFReader(XLIFFReader.VALIDATION_MAXIMAL);
			
			for ( File input : list ) {
				// Show the input path
				System.out.println("Input: "+input.getAbsolutePath());
				// Describe the kind of data we are looking for
				System.out.print("Searching for ");
				String objective = "";
				switch ( up.getRefType() ) {
				case 'f': objective = "a file"; break;
				case 'g': objective = "a group"; break;
				case 'u': objective = "a unit"; break;
				case 'n': 
					objective = "a note ";
					switch ( up.getRefContainer() ) {
					case 'f': objective += "in a file"; break;
					case 'g': objective += "in a group"; break;
					case 'u': objective += "in a unit"; break;
					default: objective += "outside a valid notes container"; break;
					}
					break;
				case 's': objective = "a segment, or an ignorable, or a source inline code or annotation"; break;
				case 't': objective = "a target inline element"; break;
				case 'd': objective = "a original data"; break;
				case 'x': objective = "a module or an extension"; break;
				}
				System.out.println(objective+" (\""+up.toString()+"\")");

				XLIFFDocument doc = new XLIFFDocument();
				doc.load(input.toURI(), XLIFFReader.VALIDATION_MAXIMAL);
				Object match = doc.fetchReference(up);
				
//				// Open the input
//				reader.open(input.toURI());
//				Object match = null;
//				// And search
//				while ( reader.hasNext() && ( match == null )) {
//					Event event = reader.next();
//					switch ( event.getType() ) {
//					case MID_FILE:
//					case START_FILE:
//					case START_GROUP:
//					case TEXT_UNIT:
//						match = event.getURIContext().matches(event, up);
//						break;
//					default:
//						// Nothing to do
//						break;
//					}
//				}
//				reader.close();

				// Show the result
				if ( match != null ) {
					System.out.println("Object found: "+match.getClass().getName());
					if ( match instanceof Unit ) { 
						printUnit((Unit)match);
					}
					else if ( match instanceof StartGroupData ) {
						printGroup((StartGroupData)match);
					}
					else if ( match instanceof CTag ) {
						if ( up.getRefType() == 'd' ) {
							printData((CTag)match);
						}
						else {
							printInlineCode((CTag)match);
						}
					}
					else if ( match instanceof MTag ) {
						printAnnotation((MTag)match);
					}
					else if ( match instanceof Segment ) {
						printSegment((Segment)match);
					}
					else if ( match instanceof Part ) {
						printIgnorable((Part)match);
					}
					else if ( match instanceof Note ) {
						printNote((Note)match);
					}
					else if ( match instanceof StartFileData ) {
						printFile((StartFileData)match);
					}
					else if ( match instanceof ExtElement ) {
						printExtElement((ExtElement)match);
					}
				}
				else {
					System.out.println("No match found.");
				}
			}			
		}
		finally {
			if ( reader != null ) reader.close();
		}
	}

	private void printUnit (Unit unit) {
		System.out.println("Some of the unit information:");
		System.out.println(" - id = "+unit.getId());
		System.out.println(" - name = "+unit.getName());
		System.out.println(" - type = "+unit.getType());
		System.out.println(" - Number of segments/ignorable elements = "+unit.getPartCount());
	}

	private void printGroup (StartGroupData sgd) {
		System.out.println("Some of the group information:");
		System.out.println(" - id = "+sgd.getId());
		System.out.println(" - name = "+sgd.getName());
		System.out.println(" - type = "+sgd.getType());
	}

	private void printData (CTag cm) {
		System.out.println("Some of the original data information:");
		System.out.println(" - id = "+cm.getDataRef());
		System.out.println(" - content = \""+cm.getData()+"\"");
	}

	private void printInlineCode (CTag cm) {
		System.out.println("Some of the inline code information:");
		System.out.println(" - id = "+cm.getId());
		System.out.println(" - type = "+cm.getType());
		System.out.println(" - marker type = "+cm.getTagType());
	}

	private void printAnnotation (MTag am) {
		System.out.println("Some of the annotation information:");
		System.out.println(" - id = "+am.getId());
		System.out.println(" - type = "+am.getType());
		System.out.println(" - ref = "+am.getRef());
		System.out.println(" - value = "+am.getValue());
	}

	private void printSegment (Segment seg) {
		System.out.println("Some of the segment information:");
		System.out.println(" - id = "+seg.getId());
		System.out.println(" - state = "+seg.getState());
		System.out.println(" - sub-state = "+seg.getSubState());
	}

	private void printNote (Note note) {
		System.out.println("Some of the note information:");
		System.out.println(" - id = "+note.getId());
		System.out.println(" - priority = "+note.getPriority());
		System.out.println(" - content = \""+note.getText()+"\"");
	}

	private void printIgnorable (Part part) {
		System.out.println("Some of the ignorable information:");
		System.out.println(" - id = "+part.getId());
	}

	private void printFile (StartFileData sfd) {
		System.out.println("Some of the file information:");
		System.out.println(" - id = "+sfd.getId());
		System.out.println(" - original = "+sfd.getOriginal());
	}

	private void printExtElement (ExtElement elem) {
		System.out.println("Some of the extension/module element information:");
		for ( ExtAttribute attr : elem.getAttributes() ) {
			System.out.println(" - " + (Util.isNoE(attr.getPrefix()) ? "" : attr.getPrefix()+":")
				+ attr.getLocalPart() +" = " + attr.getValue());
		}
	}

}
